package cn.sylinx.common.ext.res;

import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import cn.sylinx.common.lang.StringUtil;

/**
 * 类资源扫描器
 * 
 * @author han
 *
 */
public class ClasspathResourceScanner {

	/**
	 * 路径
	 */
	private String path;

	/**
	 * 后缀，例如.class
	 */
	private String postfix;

	/**
	 * 构造器
	 * 
	 * @param path
	 *            路径
	 */
	public ClasspathResourceScanner(String path) {
		this(path, null, false, null);
	}

	/**
	 * 构造器
	 * 
	 * @param path
	 *            路径
	 * @param isPackage
	 *            是否是包
	 */
	public ClasspathResourceScanner(String path, boolean isPackage) {
		this(path, null, isPackage);
	}

	/**
	 * 构造器
	 * 
	 * @param path
	 *            路径
	 * @param postfix
	 *            后缀
	 */
	public ClasspathResourceScanner(String path, String postfix) {
		this(path, postfix, false);
	}

	/**
	 * 构造器
	 * 
	 * @param path
	 *            路径
	 * @param postfix
	 *            后缀
	 * @param isPackage
	 *            是否包
	 */
	public ClasspathResourceScanner(String path, String postfix, boolean isPackage) {
		this(path, postfix, isPackage, null);
	}

	/**
	 * 构造器
	 * 
	 * @param path
	 *            路径
	 * @param postfix
	 *            后缀
	 * @param isPackage
	 *            是否包
	 * @param cl
	 *            类加载器
	 */
	public ClasspathResourceScanner(String path, String postfix, boolean isPackage, ClassLoader cl) {

		this.path = path;
		this.postfix = postfix;

		if (this.postfix == null) {
			this.postfix = ".xml";
		}

		// 如果是包名，则转换成folder路径
		if (isPackage) {
			this.path = path.replaceAll("\\.", "/");
		}

	}

	/**
	 * 获取所有资源列表
	 * 
	 * @return 资源列表
	 * @throws Exception
	 *             抛出的异常
	 */
	public List<String> getResourceNameList() throws Exception {

		return doScan(path, new ArrayList<String>());
	}

	/**
	 * 扫描类路径
	 * 
	 * @param baseFolder
	 *            基本路径
	 * @param nameList
	 *            存放结果
	 * @return 资源列表
	 * @throws Exception
	 *             异常
	 */
	private List<String> doScan(String baseFolder, List<String> nameList) throws Exception {

		List<URL> urlList = Resources.getResourceURLs(baseFolder);

		for (URL url : urlList) {

			String filePath = StringUtil.getRootPath(url);

			List<String> names = null;

			boolean isJarFile = isJarFile(filePath);
			if (isJarFile) {
				names = readFromJarFile(filePath, baseFolder);
			} else {
				names = readFromDirectory(filePath);
			}

			if (names == null) {
				return null;
			}

			for (String name : names) {

				if (isMatchFile(name)) {

					String tmpName = null;

					if (isJarFile) {
						tmpName = name;
					} else {
						tmpName = baseFolder + "/" + name;
					}

					if (!nameList.contains(tmpName)) {
						nameList.add(tmpName);
					}

				} else {
					doScan(baseFolder + "/" + name, nameList);
				}
			}
		}

		return nameList;
	}

	/**
	 * 是否是jar文件
	 * 
	 * @param name
	 *            文件名
	 * @return 是否是jar文件
	 */
	private boolean isJarFile(String name) {

		return name.endsWith(".jar");
	}

	/**
	 * 是否匹配的文件
	 * 
	 * @param name
	 *            文件名
	 * @return true or false
	 */
	private boolean isMatchFile(String name) {
		return name.endsWith(postfix);
	}

	/**
	 * 从文件路径读取文件名称
	 * 
	 * @param path
	 *            路径
	 * @return 文件名列表
	 */
	private List<String> readFromDirectory(String path) {

		File f = new File(path);
		String[] names = f.list();

		if (names == null) {
			return null;
		}

		return Arrays.asList(names);
	}

	/**
	 * 从jar包中读取文件
	 * 
	 * @param jarPath
	 *            jar路径
	 * @param splashedPackageName
	 *            包名前缀
	 * @return 资源列表
	 * @throws Exception
	 *             异常
	 */
	private List<String> readFromJarFile(String jarPath, String splashedPackageName) throws Exception {

		JarInputStream jarIn = null;

		try {

			jarIn = new JarInputStream(new FileInputStream(jarPath));

			JarEntry entry = jarIn.getNextJarEntry();

			List<String> nameList = new ArrayList<String>();

			while (entry != null) {

				String name = entry.getName();
				if (name.startsWith(splashedPackageName) && isMatchFile(name)) {
					nameList.add(name);
				}
				entry = jarIn.getNextJarEntry();
			}

			return nameList;

		} finally {

			if (jarIn != null) {

				jarIn.close();
			}
		}

	}
}
